#include "Loading/OffReader.hpp"

namespace mif
{

	OffReader::OffReader() :
		mNbVertex(0),
		mNbFace(0),
		mNbEdge(0)
	{
		//EMPTY
	}
	
	OffReader::~OffReader()
	{
		//EMPTY
	}
	
	int OffReader::Load(std::string pFileName, Mesh & pMesh)
	{
	
		std::cout << std::endl << "Loading of : " << pFileName << std::endl; 
		
		/*
			Initialisation des variables //////////////////////////////////////
		*/
		mFileName = pFileName;
		
		std::ifstream vFStream;
		std::stringstream vSS(std::stringstream::in | std::stringstream::out);
		std::string vReadingStr, vTempStr;
		double vTempCoordX, vTempCoordY, vTempCoordZ;
		int vTempIndA, vTempIndB, vTempIndC;
		
		/*
			ouverture du fichier //////////////////////////////////////////////
		*/
		vFStream.open(mFileName, std::ifstream::in);
		
		/*
			récupération du type de fichier ///////////////////////////////////
		*/
		getline(vFStream, vReadingStr);
		
		if(vReadingStr != "OFF"){
			std::cout << "ERROR::This file isn't a OFF File." << std::endl; 
			return -1;
		}
		
		/*
			récupération du nombre de vertex, edges et faces //////////////////
		*/
		getline(vFStream, vReadingStr);

		vSS.str(vReadingStr);
		vSS >> mNbVertex >> mNbFace >> mNbEdge;

		/*
			initialisation du mesh en fonction du nombre de vertex, edges et faces
		*/
		pMesh.SetVertexListLength(mNbVertex);
		pMesh.SetFaceListLength(mNbFace);
		
		/*
			récupération des vertex ///////////////////////////////////////////
		*/
		std::cout << "\tLoading Vertex ..." << std::endl;
		
		for(int i = 0; i < mNbVertex; ++i){
			getline(vFStream, vReadingStr);
			
			vSS.clear();
			vSS.str(vReadingStr);
			vSS >> vTempCoordX >> vTempCoordY >> vTempCoordZ;
			
			pMesh.SetVertex(i, Vertex(vTempCoordX, vTempCoordY, vTempCoordZ));
		}
		
		/*
			récupération des faces ////////////////////////////////////////////
		*/
		std::cout << "\tLoading Face ..." << std::endl;
		
		std::map<std::pair<int, int>, Face *> vEdgeMap;
		std::map<std::pair<int, int>, Face *>::iterator it;
		std::pair<std::map<std::pair<int, int>, Face *>::iterator,bool> vMapRes;
		
		for(int i = 0; i < mNbFace; ++i){
		
			getline(vFStream, vReadingStr);
			
			vSS.clear();
			vSS.str(vReadingStr);
			
			vSS >> vTempStr >> vTempIndA >> vTempIndB >> vTempIndC;
			
			pMesh.SetFace(i, vTempIndA, vTempIndB, vTempIndC);
			
			/*
				création du lien entre vertex et face
			*/
			pMesh.GetVertex(vTempIndA)->SetFace(*(pMesh.GetFace(i)));
			pMesh.GetVertex(vTempIndB)->SetFace(*(pMesh.GetFace(i)));
			pMesh.GetVertex(vTempIndC)->SetFace(*(pMesh.GetFace(i)));
			
			/*
				remplissage de la map de edges pour la gestion des faces voisines
			*/		
			vMapRes = vEdgeMap.insert(std::pair<std::pair<int, int>, Face *>(std::pair<int, int>(std::min(vTempIndA, vTempIndB), std::max(vTempIndA, vTempIndB)), pMesh.GetFace(i)));
			if (!vMapRes.second){	
				InitNeighborhood(pMesh.GetFace(i), vMapRes.first->second, vMapRes.first->first, 3);
				vEdgeMap.erase(vMapRes.first);
			}
			
			vMapRes = vEdgeMap.insert(std::pair<std::pair<int, int>, Face *>(std::pair<int, int>(std::min(vTempIndB, vTempIndC), std::max(vTempIndB, vTempIndC)), pMesh.GetFace(i)));
			if (!vMapRes.second){
				InitNeighborhood(pMesh.GetFace(i), vMapRes.first->second, vMapRes.first->first, 1);
				vEdgeMap.erase(vMapRes.first);
			}

			
			vMapRes = vEdgeMap.insert(std::pair<std::pair<int, int>, Face *>(std::pair<int, int>(std::min(vTempIndA, vTempIndC), std::max(vTempIndA, vTempIndC)), pMesh.GetFace(i))); 
			if (!vMapRes.second){
				InitNeighborhood(pMesh.GetFace(i), vMapRes.first->second, vMapRes.first->first, 2);		
				vEdgeMap.erase(vMapRes.first);
			}
		
		}
		
		std::cout<<"End of loading" << std::endl << std::endl;
		
		return 0;
		
	}
	
	void OffReader::InitNeighborhood(Face * pCurrentFace, Face * pNeighborFace, std::pair<int, int> pSharedEdge, int pEdgePlace)
	{		
		
		pCurrentFace->SetNeighborhood(pNeighborFace, pEdgePlace);

		const Triangle vNeighborTriangle = pNeighborFace->GetTriangle();
		
		if(!(vNeighborTriangle.a->GetID() == pSharedEdge.first || vNeighborTriangle.a->GetID() == pSharedEdge.second))
			pNeighborFace->SetNeighborhood(pCurrentFace, 1);
			
		else if(!(vNeighborTriangle.b->GetID() == pSharedEdge.first || vNeighborTriangle.b->GetID() == pSharedEdge.second))
			pNeighborFace->SetNeighborhood(pCurrentFace, 2);
			
		else
			pNeighborFace->SetNeighborhood(pCurrentFace, 3);
		
	}

}
